package com.idealighter.game.robot.common.cards;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import com.idealighter.game.dictionary.domain.HappyFiveRobotRulesDomain;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

// import org.junit.Assert;
// import org.junit.Test;

/**
 * 欢乐五张牌型计算器.
 */
public class HappyFivePowerCompare extends CardPowerCompare {
  @Override
  public CardCompare rank(List<Card> cards) {
    MapList<CardNumber, Card> numberGroup = getNumberGroup(cards);// 转成Map结构
    // 牌值
    MapList<CardSuit, Card> suitGroup = getSuitGroup(cards);// 转成Map结构 花色
    List<Card> cardsSortedByNumber = getCardsSortedByNumber(cards); // 对牌进行排序
    CardNumber straightFlushNumber = getStraightFlushNumber(suitGroup, 5);
    // Straight flush 同花顺
    if (straightFlushNumber != null) {
      return new HappyFiveCompare(CardType.STRAIGHT_FLUSH, Arrays.asList(straightFlushNumber),
          cardsSortedByNumber);
    }

    CardNumber cardNumberForFour = getCardNumberForCount(4, numberGroup);
    // Four of a kind 4+1
    if (cardNumberForFour != null) {
      return new HappyFiveCompare(CardType.FOUR_OF_A_KIND,
          calculateSameKindTie(4, cardNumberForFour, cardsSortedByNumber), cardsSortedByNumber);
    }

    List<CardNumber> fullHouseCardNumbers = getFullHouse(numberGroup);
    // Full house
    if (fullHouseCardNumbers.size() == 2) {
      return new HappyFiveCompare(CardType.FULL_HOUSE, fullHouseCardNumbers, cardsSortedByNumber);
    }

    // Flush
    CardSuit flushSuit = getFlush(suitGroup, 5);
    if (flushSuit != null) {
      return new HappyFiveCompare(CardType.FLUSH, calculateFlushTie(flushSuit, suitGroup),
          cardsSortedByNumber);
    }

    // Straight
    CardNumber straightNumber = getStraight(numberGroup, 5);
    if (straightNumber != null) {
      return new HappyFiveCompare(CardType.STRAIGHT, Arrays.asList(straightNumber),
          cardsSortedByNumber);
    }

    // Three of a kind 3+1+1
    CardNumber cardNumberForThree = getCardNumberForCount(3, numberGroup);
    if (cardNumberForThree != null) {
      return new HappyFiveCompare(CardType.THREE_OF_A_KIND,
          calculateSameKindTie(3, cardNumberForThree, cardsSortedByNumber), cardsSortedByNumber);
    }

    // Pair(s) 一对
    CardNumber cardNumberForTwo = getCardNumberForCount(2, numberGroup);
    if (cardNumberForTwo != null) {
      List<CardNumber> pairsCardNumber = getPairs(numberGroup);
      // Two pair
      if (pairsCardNumber.size() >= 2) {

        return new HappyFiveCompare(CardType.TWO_PAIR,
            calculateTwoPairsTie(pairsCardNumber, cardsSortedByNumber), cardsSortedByNumber);
      } else { // One Pair
        return new HappyFiveCompare(CardType.ONE_PAIR,
            calculateSameKindTie(2, cardNumberForTwo, cardsSortedByNumber), cardsSortedByNumber);
      }
    }

    // High Card 单牌
    return new HappyFiveCompare(CardType.HIGH_CARD, bestCardsNumberInList(cardsSortedByNumber),
        cardsSortedByNumber);
  }

  /**
   * 同花顺判断.
   *
   * @param suitGroup .
   * @param staightNum .
   * @return .
   */

  @Override
  public CardNumber getStraightFlushNumber(MapList<CardSuit, Card> suitGroup, int staightNum) {
    CardSuit flushSuit = getFlush(suitGroup, 5);
    if (flushSuit == null) {
      return null;
    }

    List<Card> cards = suitGroup.get(flushSuit);
    List<CardNumber> cardNumbers = cardsToCardNumber(cards);

    return getStraightNumber(cardNumbers, staightNum);
  }

  /**
   * 顺子判断.
   *
   * @param cardNumberList .
   * @param staightNum .
   * @return .
   */
  @Override
  public CardNumber getStraightNumber(List<CardNumber> cardNumberList, int staightNum) {
    List<CardNumber> cardNumbers = new ArrayList<>();
    cardNumbers.addAll(cardNumberList);
    CardNumber straightNumber = null;
    int straightCount = 0;
    int prevPower = 0;
    boolean flag = false;
    for (CardNumber cardNumber : cardNumbers) {
      if (cardNumber == CardNumber.ACE) {
        flag = true;
        break;
      }
    }
    if (flag) {
      cardNumbers.add(CardNumber.SEVEN);
    }
    Collections.sort(cardNumbers, cardNumberComparator);
    for (CardNumber cardNumber : cardNumbers) {
      if (cardNumber.getPower() == prevPower + 1) {
        straightCount++;
        if (straightCount >= staightNum) {
          straightNumber = cardNumber;
        }
      } else {
        straightCount = 1;
      }
      prevPower = cardNumber.getPower();
    }
    return straightNumber;
  }

  /**
   * 判断牌面.
   *
   * @param cards .
   * @param domain .
   * @param other .
   */
  public void isHave(List<Card> cards, HappyFiveRobotRulesDomain domain,
      Map<Integer, List<Card>> other) {
    List<List<Card>> li = Lists.newArrayList();
    for (List<Card> li1 : other.values()) {
      li.add(li1);
    }
    li.sort((cards1, cards2) -> {
      if (rank(cards1).compareTo(rank(cards2)) > 0) {
        return -1;
      } else {
        return 1;
      }

    });
    Map<Integer, List<Card>> otherBest = Maps.newConcurrentMap();
    otherBest.put(1, li.get(0));
    myHave(cards, otherBest, domain);
    otherIsHave(otherBest, domain);
    comPareWithOther(cards, otherBest, domain);
  }

  /**
   * 自己手上的牌 .
   *
   * @param cardList .
   * @param other .
   * @param domain .
   */
  public void myHave(List<Card> cardList, Map<Integer, List<Card>> other,
      HappyFiveRobotRulesDomain domain) {
    MapList<CardNumber, Card> numberGroup = getNumberGroup(cardList);
    MapList<CardSuit, Card> suitGroup = getSuitGroup(cardList);
    domain.setPairedMy(isPaired(numberGroup) ? 1 : 0);
    domain.setPairedMax(isPaired(numberGroup) && isPairedMax(cardList, other) ? 1 : 0);
    domain.setPairedMin(isPaired(numberGroup) && !isPairedMax(cardList, other) ? 1 : 0);
    domain.setTwoTwo(isTwoTwo(numberGroup) ? 1 : 0);
    domain.setLongFour(isLongFour(numberGroup, 4) ? 1 : 0);
    domain.setSameFour(isSameFour(suitGroup) ? 1 : 0);
    domain.setThree(isThree(numberGroup) ? 1 : 0);

  }

  /**
   * 其他玩家的牌情况.
   */
  public void otherIsHave(Map<Integer, List<Card>> cardList, HappyFiveRobotRulesDomain domain) {
    for (List<Card> cardList1 : cardList.values()) {
      MapList<CardNumber, Card> numberGroup = getNumberGroup(cardList1);
      MapList<CardSuit, Card> suitGroup = getSuitGroup(cardList1);
      if (domain.getPairedOthers() == 0) {
        if (isPaired(numberGroup)) {
          domain.setPairedOthers(1);
        }
      }
      if (domain.getTwoTwoOthers() == 0) {
        if (isTwoTwo(numberGroup)) {
          domain.setTwoTwoOthers(1);
        }
      }

      if (domain.getLongFourOthers() == 0) {
        if (isLongFour(numberGroup, 3)) {
          domain.setLongFourOthers(1);
        }
      }
      if (domain.getSameFourOthers() == 0) {
        if (isSameFour(suitGroup)) {
          domain.setSameFourOthers(1);
        }
      }

      if (domain.getThreeOthers() == 0) {
        if (isThree(numberGroup)) {
          domain.setThreeOthers(1);
        }
      }
    }
  }

  /**
   * 和其他人比较.
   *
   * @param myCards .
   * @param other .
   * @param domain .
   */
  public void comPareWithOther(List<Card> myCards, Map<Integer, List<Card>> other,
      HappyFiveRobotRulesDomain domain) {
    List<Card> cardList = Lists.newArrayList(); // ->除去底牌
    cardList.addAll(myCards.subList(1, myCards.size()));
    if (myCards.size() > 2) {
      domain.setFirst(isFirst(cardList, other) ? 1 : 0);
      domain.setBiggest(isBiggest(cardList, other) ? 1 : 0);
      domain.setWin1(isWin1(myCards, other) ? 1 : 0);
    }
  }

  /**
   * 是否是最大牌面.
   *
   * @param myCard .
   * @param other .
   * @return .
   */

  public boolean isFirst(List<Card> myCard, Map<Integer, List<Card>> other) {
    for (List<Card> cards : other.values()) {
      if (rank(myCard).compareTo(rank(cards)) < 0) {
        return false;
      }
    }
    return true;
  }

  /**
   * 是否有最大值的牌(单牌).
   *
   * @param myCard .
   * @param other .
   * @return .
   */
  public boolean isBiggest(List<Card> myCard, Map<Integer, List<Card>> other) {
    for (List<Card> cardList : other.values()) {
      if (getMaxCard(myCard).getNumber().getPower() <= getMaxCard(cardList).getNumber()
          .getPower()) {
        if (getMaxCard(myCard).getNumber().getPower() == getMaxCard(cardList).getNumber()
            .getPower()) {
          if (getMaxCard(myCard).getSuit().getPower() < getMaxCard(cardList).getSuit().getPower()) {
            return false;
          }
        }
        return false;
      }
    }
    return true;
  }

  /**
   * 手牌+明牌是否输对方明牌.
   *
   * @param myCard .
   * @param other .
   * @return .
   */
  public boolean isWin1(List<Card> myCard, Map<Integer, List<Card>> other) {
    for (List<Card> cards : other.values()) {
      if (rank(myCard).compareTo(rank(cards)) > 0) {
        return false;
      }
    }
    return true;
  }

  /**
   * 获取最大牌值的牌.
   *
   * @param cardList .
   * @return .
   */
  public Card getMaxCard(List<Card> cardList) {
    Card cardTemp = null;
    for (Card card : cardList) {
      if (cardTemp == null) {
        cardTemp = card;
      }
      if (cardTemp.compareTo(card) <= 0) {
        if (cardTemp.compareTo(card) == 0) {
          if (cardTemp.getSuit().getPower() < card.getSuit().getPower()) {
            cardTemp = card;
          }
        } else {
          cardTemp = card;
        }
      }

    }
    return cardTemp;
  }

  /**
   * 是否有对子.
   *
   * @return .
   */
  private boolean isPaired(MapList<CardNumber, Card> numberGroup) {
    CardNumber cardNumberForPaired = getCardNumberForCount(2, numberGroup);
    if (cardNumberForPaired != null) {
      return true;
    }
    return false;
  }

  /**
   * 对子是否是最大牌.
   *
   * @param cardList .
   * @param cardMy .
   * @return .
   */
  private boolean isPairedMax(List<Card> cardMy, Map<Integer, List<Card>> cardList) {
    MapList<CardNumber, Card> numberGroup = getNumberGroup(cardMy);
    CardNumber cardNumberForTwo = getCardNumberForCount(2, numberGroup);
    if (cardNumberForTwo == null) {
      return false;
    }
    for (List<Card> cardList1 : cardList.values()) {
      if (cardNumberForTwo.getPower() < getMaxCard(cardList1).getNumber().getPower()) {
        return false;
      }
    }
    return true;
  }

  /**
   * 是否有两对.
   *
   * @param cards .
   * @return .
   */
  private boolean isTwoTwo(MapList<CardNumber, Card> cards) {
    CardNumber cardNumberForPaired = getCardNumberForCount(2, cards);
    if (cardNumberForPaired != null) {
      List<CardNumber> pairsCardNumber = getPairs(cards);
      if (pairsCardNumber.size() >= 2) {
        return true;
      }
    }
    return false;
  }
  // @Test
  // public void EgTest(){
  // List<Card> cards = Lists.newArrayList();
  // List<Card> cards1 = Lists.newArrayList();
  //
  //// cards.add(new Card(CardSuit.SPADE, CardNumber.ACE));
  // cards.add(new Card(CardSuit.SPADE, CardNumber.JACK));
  // cards.add(new Card(CardSuit.HEART, CardNumber.ACE));
  //
  // cards.add(new Card(CardSuit.DIAMOND, CardNumber.JACK));
  // cards.add(new Card(CardSuit.HEART, CardNumber.JACK));
  //
  //
  // cards1.add(new Card(CardSuit.SPADE, CardNumber.QUEEN));
  // cards1.add(new Card(CardSuit.SPADE, CardNumber.TEN));
  // cards1.add(new Card(CardSuit.HEART, CardNumber.QUEEN));
  // cards1.add(new Card(CardSuit.DIAMOND, CardNumber.TEN));
  //
  // Map<Integer, List<Card>> other = Maps.newConcurrentMap();
  // other.put(1, cards1);
  //
  //
  // Assert.assertEquals(false, isFirst(cards, other));
  //
  //// MapList<CardNumber, Card> numberGroup = getNumberGroup(cards);
  //// Assert.assertEquals(true, isTwoTwo(numberGroup));
  //
  // }
  //

  /**
   * 是否有三条.
   *
   * @return .
   */
  private boolean isThree(MapList<CardNumber, Card> cards) {
    CardNumber cardNumberForThree = getCardNumberForCount(3, cards);
    if (cardNumberForThree != null) {
      return true;
    }
    return false;
  }

  /**
   * 是否有N张顺子.
   *
   * @return .
   */
  private boolean isLongFour(MapList<CardNumber, Card> cards, int num) {
    CardNumber straightNumber = getStraight(cards, num);
    if (straightNumber != null) {
      return true;
    }
    return false;
  }

  /**
   * 是否有4张以上的同花.
   *
   * @return .
   */
  private boolean isSameFour(MapList<CardSuit, Card> cards) {
    CardSuit flushSuit = getFlush(cards, 4);
    if (flushSuit != null) {
      return true;
    }
    return false;
  }
}
